#The first stateful generator:take()

def take(count, iterable):
    """Take items from the front of an iterable.

    Args:
        count: The maximum number of items to retrieve. iterable: The source of the items.

    Yields:
    At most 'count' items from 'iterable'.
    """
    counter = 0
    for item in iterable:
        if counter == count: 
            return
        counter += 1 
        yield item

def run_take():
    items = [2, 4, 6, 8, 10]
    for item in take(3, items): 
        print(item)

run_take()


#The second stateful generator: distinct()

def distinct(iterable):
    """Return unique items by eliminating duplicates.

    Args:
        iterable: The source of the items.

    Yields:
        Unique elements in order from 'iterable'.
    """
    seen = set()
    for item in iterable: 
        if item in seen:
            continue 
        yield item 
        seen.add(item)

def run_distinct():
    items = [5, 7, 7, 6, 5, 5]
    for item in distinct(items): 
        print(item)

run_distinct()
 

#Lazy generator pipelines
def run_pipeline():
    items = [3, 6, 6, 2, 1, 1]
    for item in take(3, distinct(items)): 
        print(item)

#Non lazy version (make a list of items before taking them)
def run_pipeline():
    items = [3, 6, 6, 2, 1, 1]
    for item in take(3, list(distinct(items))): 
        print(item)


#Laziness and the infinite

def lucas():
    yield 2
    a  =  2
    b  =  1
    while True:
        yield  b
        a, b = b, a + b

for x in lucas():
    print(x)

#Note: In windows use CTRL+C to exit!

